/**
 +-------------------------------------------------------------------
 * FontScroll - 文字行向上滚动插件
 * 说明： 
 * 		本项目参考 https://gitee.com/kzm/FontScroll 项目修改而来，
 * 		初始化后会将ul复制一遍，因此可能会导致一些选择错误（如根据ul的id查询ul节点），需注意！
 * 		1.将jquery扩展件改成了独立组件（当然还是会依赖jquery），方便在同一页面里支持多个FontScroll；
 *		2.添加了新的参数进行控制（如speed、skip、pauseOnHover、startOnInit等）；
 *		3.添加了些的方法（启停、暂停继续、销毁等）；
 * 		4.计算偏移margin时，每行的高度单独计算，避免行高不一致导致的偏移问题；
 * 		5.添加elH、ulH的判断，动态计算偏移高度，支持滚动容器div动态修改高度；
 * 		6.添加动态增减li的方法，可以在运行中动态增减行数（但不推荐这么做）；
 * 		7.修复了原项目的一些bug；
 * 作者：konger123 （随枫的落叶 konger@126.com）
 * gitee：https://gitee.com/konger123/FontScroll
 * 
 * 原项目github：https://gitee.com/kzm/FontScroll
 * 原作者：kongzhim
 +-------------------------------------------------------------------
 * @version    1.0.1
 * @since      2020.09.05
 * @author     konger123 <konger@126.com>
 * @gitee     https://gitee.com/konger123/FontScroll
 +-------------------------------------------------------------------
 */
 
var FontScroll = (function() {
	
	/**
	 * 创建方法
	 * @param target 滚动目标div的ID或选择器
	 * @options 参数：
	 * 		time：每次滚动的时间间隔(单位：毫秒)，默认3000
	 * 		speed：滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
	 * 		rowClass：滚动选中行的样式，默认：fontColor
	 * 		num：滚动选中的行序号，默认：0 表示第一行
	 * 		skip：滚动时上面留白显示的行数，默认0
	 * 		pauseOnHover：鼠标经过时是否暂停，默认：true
	 *		startOnInit：初始化后是否自动运行，默认：true
	 * 		init：是否默认初始化，默认true
	 */
	var FontScroll = function(target, options) {
		var _this = this;
        this.version = '1.0.1';
		this.defaults = {
            time: 3000,	//每次滚动的时间间隔(单位：毫秒)，默认3000
			speed: 500,	//滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
			timeoutMethod: 'interval', //延时时间方式：interval-使用setInverval处理；timeout-使用setTimeout处理
            rowClass: 'fontColor', //滚动选中行的样式，默认：fontColor
            num: 0,  //滚动时选中的行序号，默认：0 表示第一行
			skip: 0,	//滚动时上面留白显示的行数，默认0
			scrollBehavior: 'rowFirst', //滚动动画与行样式的先后顺序：rowFirst先加行样式，再动画滚动；scrollFirst先动画滚动，再加行样式
            pauseOnHover: true,	//鼠标经过时是否暂停，默认：true
			startOnInit: true,	//初始化后是否自动运行，默认：true
			init: true,	//是否默认初始化
			ul: 'ul', //滚动的行的外部容器（选择器），用于获取滚动区域的实际高度
			li: 'li', //滚动行（选择器）
        };
        this.options = $.extend({}, this.defaults, options);
		if(this.options.speed>=this.options.time) {//防止给定的动画时间不足
			this.options.speed = this.options.time - 10;
		}
		
        this.target = target;
        this.el = (typeof target === 'string') ? $(target) : $(target);
		this.ul = null;//ul对象
		this.li = null;
		this.length = 0;//li的个数
		this.liArr = new Array();
		this.ulClone = null;
		this.liClone = null;
		
		this.elH = 0;
		this.ulH = 0;
		
        this.timeID = null;//timer的ID
		this.cursor = this.options.num;
		this.ulMargin = 0;
		this.inited = false;
		
		if(this.options.init) {
			this.init();
		}
		
		if(this.options.startOnInit) {
			this.start();
		}
	}
	
	/**
	 * 初始化
	 */
	FontScroll.prototype.init = function() {
		if(this.el==undefined || this.el==null) {
			throw new Error("初始化失败，对象是否已被销毁？请检查！");
		}
		
		if(this.inited) {
			return this;
		}
		
		this.ul = this.el.find(this.options.ul).first();//ul对象
		if(this.ul == null || this.ul.length < 1) {
			throw new Error("初始化失败，未找到'"+this.options.ul+"'对象");
		}
		this.li = this.ul.find(this.options.li);
		this.length = this.li.length;
		this.liArr = new Array();
		this.ulClone = this.ul.clone();
		this.ulClone.insertAfter(this.ul);
		this.liClone = this.ulClone.find(this.options.li);
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		if(this.elH < this.ulH) {
			this.ulClone.show();
		} else {
			this.ulClone.hide();
		}
		
        this.timeID = null;//timer的ID
		this.cursor = this.options.num;
		this.ulMargin = 0;
		
		if(this.length > 0) {
			this.cursor = this.options.num % this.length;
			
			for(var i=0; i<this.li.length; i++) {
				this.liArr.push($(this.li[i]));
			}
			for(var i=0; i<this.liClone.length; i++) {
				this.liArr.push($(this.liClone[i]));
			}
			
			this.li.removeClass(this.options.rowClass);
			this.liClone.removeClass(this.options.rowClass);
			this.liArr[this.cursor].addClass(this.options.rowClass);
			
			var newMargin = 0;
			if(this.elH < this.ulH) {
				var liH = 0;
				for(var i=this.options.skip; i<this.liArr.length && i<this.cursor; i++) {
					liH =  this.liArr[i].outerHeight(true);
					newMargin = accAdd(newMargin, liH);
				}
				this.ulMargin = newMargin;
				this.ul.animate({marginTop: (this.ulMargin==0 ? 0 : '-'+this.ulMargin)}, this.options.speed);
			}
		}
		
		this.inited = true;
		return this;
	}
	
	/**
	 * 更新参数
	 * @param options 新的参数
	 * @param immediatelyEffect 新参数是否立即生效
	 */
	FontScroll.prototype.updateOptions = function(options, immediatelyEffect) {
		this.options = $.extend({}, this.options, options);
		
		if(immediatelyEffect) {
			if(this.timeID!=null) {
				//如果正在运行的话，使新参数立即生效
				this.pause();
				this.resume();
			}
		}
		return this;
	}
	
	/**
	 * 开始运行
	 */
	FontScroll.prototype.start = function() {
		if(this.timeID != null) {
			return this;
		}
		if(this.length < 1) {
			return this;
		}
		if(!this.inited) {
			this.init();
		}
		
		var _this = this;
		
		if(_this.options.timeoutMethod == 'interval') {
			_this.timeID = setInterval(function() {
				_this.up();
			}, _this.options.time);
		} else {
			_this.timeID = setTimeout(function() {
				_this.up();
			}, _this.options.time);
		}
        
		if(_this.options.pauseOnHover) {
			_this.ul.hover(function(){
				if(_this.timeID != null) {
					if(_this.options.timeoutMethod == 'interval') {
						clearInterval(_this.timeID);
					} else {
						clearTimeout(_this.timeID);
					}
					_this.timeID = null;
				}
			},function(){
				if(_this.options.timeoutMethod == 'interval') {
					_this.timeID = setInterval(function() {
						_this.up();
					}, _this.options.time);
				} else {
					_this.timeID = setTimeout(function() {
						_this.up();
					}, _this.options.time);
				}
			});
		}
        
        return this;
	}
	
	/**
	 * 继续
	 * @returns {Number} timeID
	 */
	FontScroll.prototype.resume = function() {
        return this.start();
	}
	
	/**
	 * 向上滚动一行
	 * @param resetDelay 当延时方法为timeout时，是否在滚动后要重新计算延时
	 */
	FontScroll.prototype.up = function(resetDelay) {
		if(!this.inited) {
			this.init();
			return this;
		}
		
		var _this = this;

		_this.cursor++;
		
		var newMargin = 0;
		if(_this.elH < _this.ulH) {//需要设置偏移
			for(var i=_this.options.skip; i<_this.liArr.length && i<_this.cursor; i++) {
				liH =  _this.liArr[i].outerHeight(true);
				newMargin = accAdd(newMargin, liH);
			}
			_this.ulMargin = newMargin;
		}
		
		if(_this.liArr.length > 0) {
			
			//先加行样式，后滚动
			if(_this.options.scrollBehavior == "rowFirst") {
				_this.li.removeClass(_this.options.rowClass);
				_this.liClone.removeClass(_this.options.rowClass);
				_this.liArr[_this.cursor].addClass(_this.options.rowClass);
			}
			
			//滚动动画
			_this.ul.animate({marginTop: (_this.ulMargin==0 ? 0 : '-'+_this.ulMargin)}, _this.options.speed, function() {
				if(_this.cursor - _this.options.skip >= _this.length ) {
					var oldCur = _this.cursor;
					_this.cursor = _this.options.skip;
					_this.ulMargin = 0;
					for(var i=_this.options.skip; i<_this.liArr.length && i<_this.cursor; i++) {
						_this.ulMargin = accAdd(_this.ulMargin, _this.liArr[i].outerHeight(true));
					}
					
					_this.ul.animate({marginTop: (_this.ulMargin==0 ? 0 : '-'+_this.ulMargin)}, 10, function() {
						_this.li.removeClass(_this.options.rowClass);
						_this.liClone.removeClass(_this.options.rowClass);
						_this.liArr[_this.cursor].addClass(_this.options.rowClass);
					});
				} else {
					//先滚动，后加行样式
					if(_this.options.scrollBehavior != "rowFirst") {
						_this.li.removeClass(_this.options.rowClass);
						_this.liClone.removeClass(_this.options.rowClass);
						_this.liArr[_this.cursor].addClass(_this.options.rowClass);
					}
				}
			});
		}
		
		if(_this.timeID != null) {
			if(_this.options.timeoutMethod == 'timeout') {
				clearTimeout(_this.timeID);
				_this.timeID = setTimeout(function() {
					_this.up();
				}, _this.options.time);
			} else {
				if(resetDelay) {
					clearInterval(_this.timeID);
					_this.timeID = setInterval(function() {
						_this.up();
					}, _this.options.time);
				}
			}
		}
		return this;
	}
	
	/**
	 * 暂停
	 */
	FontScroll.prototype.pause = function() {
		if(this.options.pauseOnHover) {
			//取消hover事件
			this.ul.unbind('mouseenter').unbind('mouseleave');
		}
		if(this.timeID != null) {
			if(this.options.timeoutMethod == 'interval') {
				clearInterval(this.timeID);
			} else {
				clearTimeout(this.timeID);
			}
			this.timeID = null;
		}
		return this;
	}
	
	/**
	 * 停止
	 */
	FontScroll.prototype.stop = function() {
		if(!this.inited) {
			return this;
		}
		
		var _this = this;

		//取消事件
		if(this.options.pauseOnHover) {
			//取消hover事件
			this.ul.unbind('mouseenter').unbind('mouseleave');
		}
		//停止滚动
		if(this.timeID != null) {
			if(this.options.timeoutMethod == 'interval') {
				clearInterval(this.timeID);
			} else {
				clearTimeout(this.timeID);
			}
			this.timeID = null;
		}
		
		//滚动还原
		this.cursor = this.options.num;
		this.ulMargin = 0;
		
		if(this.length > 0) {
			this.cursor = this.options.num % this.length;
			
			for(var i=0; i<this.li.length; i++) {
				this.liArr.push($(this.li[i]));
			}
			for(var i=0; i<this.liClone.length; i++) {
				this.liArr.push($(this.liClone[i]));
			}
			
			this.li.removeClass(this.options.rowClass);
			this.liClone.removeClass(this.options.rowClass);
			this.liArr[this.cursor].addClass(this.options.rowClass);
			
			var newMargin = 0;
			if(this.elH < this.ulH) {
				var liH = 0;
				for(var i=this.options.skip; i<this.liArr.length && i<this.cursor; i++) {
					liH =  this.liArr[i].outerHeight(true);
					newMargin = accAdd(newMargin, liH);
				}
				this.ulMargin = newMargin;
			}
		}
		
		this.ul.animate({marginTop: (_this.ulMargin==0 ? 0 : '-'+_this.ulMargin)}, 10);
		
		return this;
	}
	
	/**
	 * 销毁对象。销毁后对象会变成"{}"
	 */
	FontScroll.prototype.destroy = function() {
		this.stop();
		this.ulClone.remove();
		for(var a in this) {
			delete this[a];
		}
	}
	
	/**
	 * 添加li
	 * @param o 要添加的li对象。可以是"<li>...</li>"这样的字符串，也可以是dom对象
	 */
	FontScroll.prototype.addLi = function(o) {
		var dom = $(o);
		this.ul.append(dom);
		this.li = this.ul.find(this.options.li);
		this.length = this.li.length;
		this.ulClone.append(dom.clone());
		this.liClone = this.ulClone.find(this.options.li);
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		if(this.elH < this.ulH) {
			this.ulClone.show();
		} else {
			this.ulClone.hide();
		}
		
		this.liArr = new Array();
		for(var i=0; i<this.li.length; i++) {
			this.liArr.push($(this.li[i]));
		}
		for(var i=0; i<this.liClone.length; i++) {
			this.liArr.push($(this.liClone[i]));
		}
		
		return this;
	}
	
	/**
	 * 移除li
	 * @param selector 要移除li的选择器。如"#li_1"、"li:first"、"li:last"、"li:eq(2)"等等
	 */
	FontScroll.prototype.removeLi = function(selector) {
		this.ul.find(selector).remove();
		this.ulClone.find(selector).remove();
		this.li = this.ul.find(this.options.li);
		this.liClone = this.ulClone.find(this.options.li);
		this.length = this.li.length;
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		if(this.elH < this.ulH) {
			this.ulClone.show();
		} else {
			this.ulClone.hide();
		}
		
		this.liArr = new Array();
		for(var i=0; i<this.li.length; i++) {
			this.liArr.push($(this.li[i]));
		}
		for(var i=0; i<this.liClone.length; i++) {
			this.liArr.push($(this.liClone[i]));
		}
		
		if(this.cursor == this.length - 1) {
			this.cursor = 0;
		}
		
		return this;
	}
	
	/**
	 * 加法函数，用来得到精确的加法结果
	 * 说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
	 * 调用：accAdd(arg1,arg2)
	 * 参数：arg1被加数，arg2加数
	 * 返回值：arg1加上arg2的精确结果
	 */
	var accAdd = function(arg1, arg2) {
		var r1, r2, m;
		try {
			r1 = arg1.toString().split(".")[1].length;
		} catch (e) {
			r1 = 0;
		}
		try {
			r2 = arg2.toString().split(".")[1].length;
		} catch (e) {
			r2 = 0;
		}
		m = Math.pow(10, Math.max(r1, r2));
		return (arg1 * m + arg2 * m) / m;
	}
	
	return FontScroll;
}());